﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CORE = Cosmos.Core;
using UI = DimOS.DimSH.Interface;
using Cosmos.Core.IOGroup;
using DimOS.Constants;
using DimOS.DimSH;
using DimOS.Internals;

namespace DimOS.VTS
{
    public class Screen : Base
    {
        private readonly List<CharColor[]> _scrollUpBuffer;
        private readonly List<CharColor[]> _scrollDownBuffer;
        private readonly int _lastRow;
        private readonly UInt16 _nullCharacter;
        private readonly UInt16 _outputColumns;
        private readonly UInt32 _address;
        private readonly UInt32 _scrollSize; //1 Row
        private readonly UInt16 _clearSize; //1 Row

        public byte ScrollStatus {
            get
            {
                if (_scrollUpBuffer.Count == 0 && _scrollDownBuffer.Count != 0) return 1;
                if (_scrollUpBuffer.Count != 0 && _scrollDownBuffer.Count == 0) return 0;
                if (_scrollUpBuffer.Count != 0 && _scrollDownBuffer.Count != 0) return 2;
                if (_scrollUpBuffer.Count == 0 && _scrollDownBuffer.Count == 0) return 3;
                return 4;
            } }

        public Screen()
        {
            _nullCharacter = 0x00;
            _lastRow = Rows - 3;
            _outputColumns = (UInt16)(Columns - UI.InfoColumns);
            _address = (UInt32)(Columns * _lastRow * 2);
            _scrollSize = (UInt32)(_outputColumns * 2);
            _clearSize = (UInt16)(_outputColumns / 2);
            
            Position = new Point(0, Rows - 1);
            CursorVisibility = false;

            CursorSize = GetCursorSize();
            Foreground = GetForeground();
            Background = GetBackground();

            _buffer = new CORE.MemoryBlock((UInt32)(BufferBase + ((Screens.List.Count + 1) * BufferSize)), BufferSize);
            _buffer.Fill(_nullCharacter);

            _scrollUpBuffer = new List<CharColor[]>();
            _scrollDownBuffer = new List<CharColor[]>();
        }

        public UInt16 Rows { get { return TotalRows; } /*Setter cannot be implemented yet*/}
        public UInt16 Columns { get { return TotalColumns; } /*Setter cannot be implemented yet*/}

        public ConsoleColor Foreground { get; set; }
        public ConsoleColor Background { get; set; }

        public Point Position;
        public int X { get { return Position.X; } set { Position.X = value; } }
        public int Y { get { return Position.Y; } set { Position.Y = value; } }

        public int CursorSize { get; set; }
        public bool CursorVisibility { get; set; }

        private CORE.MemoryBlock _buffer;
        public CORE.MemoryBlock Buffer { get { return _buffer; } set { _buffer = value; } }

        public void Clear(bool full = false)
        {
            for (int i = 0; i < (Rows - 2) * 2; i += 2)
                _buffer.Fill((UInt32)(Columns * i), _clearSize, _nullCharacter);
        }

        public void ScrollUp(bool reflectChanges = false)
        {
            var count = _scrollUpBuffer.Count;
            if (count != 0)
            {
                if (reflectChanges)
                {
                    ScrollBackward();
                    for (int i = 0; i < _outputColumns; i++)
                    {
                        var temp = _scrollUpBuffer[count - 1][i];
                        SetCharacter(i, 0, temp.Character, temp.Color);
                    }
                }

                var row = new CharColor[_outputColumns];
                for (int i = 0; i < _outputColumns; i++) row[i] = this[i, _lastRow];
                _scrollDownBuffer.Add(row); 

                for (int i = _lastRow * 2 - 2; i >= 0; i -= 2)
                    _buffer.MoveDown((UInt32)(Columns * (i + 2)),
                        (UInt32)(Columns * i), _scrollSize);

                for (int i = 0; i < _outputColumns; i++)
                    this[i, 0] = _scrollUpBuffer[count - 1][i];

                _scrollUpBuffer.RemoveAt(count - 1);
            }
        }

        public void ScrollDown(bool reflectChanges = false)
        {
            var count = _scrollDownBuffer.Count;
            if (count != 0)
            {
                if (reflectChanges)
                {
                    ScrollForward();
                    for (int i = 0; i < _outputColumns; i++)
                    {
                        var temp = _scrollDownBuffer[count - 1][i];
                        SetCharacter(i, _lastRow, temp.Character, temp.Color);
                    }
                }

                var row = new CharColor[_outputColumns];
                for (int i = 0; i < _outputColumns; i++) row[i] = this[i, 0];
                _scrollUpBuffer.Add(row);

                for (int i = 0; i < _lastRow*2; i += 2)
                    _buffer.MoveDown((UInt32) (Columns*i),
                        (UInt32) (Columns*(i + 2)), _scrollSize);

                for (int i = 0; i < _outputColumns; i++)
                    this[i, _lastRow] = _scrollDownBuffer[count - 1][i];

                _scrollDownBuffer.RemoveAt(count - 1);
            }
        }

        public void Scroll(bool reflectChanges = false)
        {
            var row = new CharColor[_outputColumns];
            for (int i = 0; i < _outputColumns; i++) row[i] = this[i, 0];
            _scrollUpBuffer.Add(row);

            if (reflectChanges) { ScrollForward(); UI.DrawScrollIndicator(); }

            for (int i = 0; i < _lastRow * 2; i += 2)
                _buffer.MoveDown((UInt32)(Columns * i),
                    (UInt32)(Columns * (i + 2)), _scrollSize);
            _buffer.Fill(_address, _clearSize, _nullCharacter);
        }

        public void ScrollEnd(bool reflectChanges = false)
        { for (int i = _scrollDownBuffer.Count; i > 0; i--) ScrollDown(reflectChanges); }

        public CharColor this[int x, int y]
        { 
            get
            {
                var address = (UInt32)((x + y * Columns) * 2);
                return new CharColor((char)_buffer[address], (byte)_buffer[address + 1]);
            }
            set
            {
                var address = (UInt32)((x + y * Columns) * 2);
                _buffer[address] = (byte)value.Character;
                _buffer[address + 1] = value.Color;
            }
        }

        public void Display(ConsoleColor foreground = Colors.SectionInputForeground,
            ConsoleColor background = Colors.SectionInputBackground)
        {
            for (int i = 0; i < Rows - 2; i++)
                for (int j = 0; j < _outputColumns; j++)
                { var temp = this[j, i]; SetCharacter(j, i, temp.Character, temp.Color); }

            SetCursorSize(CursorSize);
            SetForeground(foreground);
            SetBackground(background);
            if (CursorVisibility) ShowCursor(); else HideCursor();
        }
    }
}
